
/*
	coverage trace stub

	it depend on the MS compiler, for other compiler(eg:gcc) this code can't work
	for c++ replace "{" to "{_XXXSTUB;" and compile
	for c replace "{" to "{_XXXSTUB;" will generate some compile error, find it place "_XXXSTUB" under the variable declear

	you can get test result by call XXXStubOutPutInfo at some place of the test program
	or use some tools read the memory of ".cover" section and anlysis it, the ".cover" section address can be find by dumpbin

	add a define of _TRACE_STUB_IMP or _COVER_STUB_IMP or _STUB_IMP (=_TRACE_STUB_IMP + _COVER_STUB_IMP) or  KEN_LIB_IMP (=_STUB_IMP) in one of the project file to generate code

*/

#ifndef _STUBS_H_
#define _STUBS_H_

#include <stdarg.h>

#include "miscutil.h"
#include "FastLock.h"


#pragma warning( push )
#pragma warning(disable : 4996)

#ifdef __cplusplus
extern "C" {
#endif

struct CoverPoint
{
#define COVERPOINT_TAG	0x54505643	//CVPT
	DWORD dwTag;

	const TCHAR* pszFile;
	const TCHAR* pszLine;	// not use dword as the type of line, 
							// for __LINE__ compiler will generate a variable,
							// so this member can't be init at compile time, 
							// and must be evaluated every time
	DWORD dwCnt;
};

////////////////////////////////////////////////////////////////////////
//  cover stub user interface
//

typedef BOOL (WINAPI * pfnCoverStubFn)(VOID* pv, struct CoverPoint* pCp);
typedef BOOL (WINAPI * pfnTraceStubFn)(VOID* pv, struct TraceInfo* pTi);
typedef  VOID (WINAPI * pfnOutMsg)(const TCHAR *pszMsg);

// add _COVERSTUB in the place of cover pointer
#define _COVERSTUB _COVERSTUB1(__LINE__)



// ForEachCoverStub will return immediatley if pfnSubFn return false
BOOL ForEachCoverStub(pfnCoverStubFn pfn, VOID* pv);
VOID CoverStubOutPutInfo(pfnOutMsg pfnOutMessage);
VOID CoverStubClear();
struct CoverStubInterface
{
	VOID (*pfn_CoverStubClear)();
	VOID (*pfn_CoverStubOutPutInfo)(pfnOutMsg pfnOutMessage);
	BOOL (*pfn_ForEachCoverStub)(pfnCoverStubFn pfn, VOID* pv);
};
VOID GetCoverStubInterface(struct CoverStubInterface* pCvStubIntf);

//
// add stubs for key word
// ! note be careful for such cases: 
// ! if() return xxx
// ! this well case return always for macro expand as this: if() {coversttub} return xxx
// ! hung
//
#if defined(_COVER_STUB_GOTO)|defined(_COVER_STUB_ALLKEYWORD)
#define goto _COVERSTUB; goto
#endif	//#if defined(_COVER_STUB_GOTO)|defined(_COVER_STUB_ALLKEYWORD)

#if defined(_COVER_STUB_BREAK)|defined(_COVER_STUB_ALLKEYWORD)
#define break _COVERSTUB; break
#endif	//#ifndef _COVER_NO_BREAK#if defined(_COVER_STUB_BREAK)|defined(_COVER_STUB_ALLKEYWORD)

#if defined(_COVER_STUB_CONTINUE)|defined(_COVER_STUB_ALLKEYWORD)
#define continue _COVERSTUB; continue
#endif	//#if defined(_COVER_STUB_CONTINUE)|defined(_COVER_STUB_ALLKEYWORD)

#if defined(_COVER_STUB_RETURN)|defined(_COVER_STUB_ALLKEYWORD)
#define return _COVERSTUB; return
#endif	//#if defined(_COVER_STUB_RETURN)|defined(_COVER_STUB_ALLKEYWORD)


////////////////////////////////////////////////////////////////////////
//  trace stub user interface
// ! NOT THREAD SAFE !
//

// define statck size
#ifndef TRACE_STUB_STACK_SIZE_KB
#define TRACE_STUB_STACK_SIZE_KB 256
#endif	//#ifndef TRACE_STUB_STACK_SIZE_KB
#ifndef TRACE_STUB_INIT_ENABLE
#define TRACE_STUB_INIT_ENABLE 1		// enable 
#endif	//#ifndef TRACE_STUB_INIT_ENABLE
#ifndef TRACE_STUB_INIT_OPTION
#define TRACE_STUB_INIT_OPTION 1		// enable  perfermence count
#endif	//#ifndef TRACE_STUB_INIT_OPTION

// add _TRACESTUB or _TRACESTUB_INFO in the place of trace pointer
#define _TRACESTUB _TRACESTUB1(__LINE__, (0))
#define _TRACESTUB_INFO(expr) _TRACESTUB1(__LINE__, expr)

#define _TRACESTUB_IF(cond)  if(cond){_TRACESTUB}
#define _TRACESTUB_INFO_IF(cond, expr) if(cond){_TRACESTUB_INFO(expr)}

struct TraceInfo
{
#define TRACE_INFO_TAG	0x4954	// "TI"
	WORD wTag;
	WORD wExpandDwSize;
	struct CoverPoint* pCp;
	DWORD dwPerformCnt;
};

VOID TraceStubReset();
VOID TraceStubEnable();
VOID TraceStubDisable();
#define TRACE_STUB_OPTION_PERF		(1<<0)
VOID TraceStubSetOption(DWORD dwOption);
DWORD TraceStubGetOption();
VOID TraceStubGetInfo(DWORD* pdwBufSize, DWORD* pdwTraceNumb, DWORD* pdwDropped);
BOOL ForEachTraceStub(pfnTraceStubFn pfn, VOID* pv);
VOID TraceStubOutPutInfo(pfnOutMsg pfnOutMessage);


struct TraceInfo* WINAPI TraceStubExpand(WORD dwExpandDwSize, ...);


////////////////////////////////////////////////////////////////////////
//  help defines
//

#pragma section(".cover$__a",read,write)
#pragma section(".cover$__c",read,write)
#pragma section(".cover$__z",read,write)

//
//  cover help defines
//

//#ifdef __cplusplus
#define _COVERSTUB2(line) {__declspec(allocate(".cover$__c")) static struct CoverPoint _coverstub##line = {COVERPOINT_TAG, __TFILE__, __TCLINE__, 0};_coverstub##line.dwCnt++;}
#define _COVERSTUB1(line) _COVERSTUB2(line)
//#define _COVERSTUB _COVERSTUB1(__LINE__)
//#else

/*
extern struct CoverPoint _coverstub[];

#define _COVERSTUB2(count) _coverstub[count].dwCnt++;_coverstub[count].pszLine=__TCLINE__;_coverstub[count].pszFile=__TFILE__;
#define _COVERSTUB1(count) _COVERSTUB2(count)
#define _COVERSTUB _COVERSTUB1(__COUNTER__)

#define _COVERSTUB_FILE_END2(count)  __declspec(allocate(".cover$__c")) static struct CoverPoint _coverstub[count]={0};
#define _COVERSTUB_FILE_END1(count)  _COVERSTUB_FILE_END2(count)
#define _COVERSTUB_FILE_END _COVERSTUB_FILE_END1(__COUNTER__)
*/
//#endif


//
//  trace help defines
//

#define _TRACESTUB2(line, expr) \
{	\
	__declspec(allocate(".cover$__c")) static struct CoverPoint _coverstub##line = {COVERPOINT_TAG, __TFILE__, __TCLINE__, 0};	\
	struct TraceInfo* pti=TraceStubExpand expr;	\
	_coverstub##line.dwCnt++;	\
	if(pti){	\
		pti->pCp=&_coverstub##line;	\
	}	\
}
#define _TRACESTUB1(line, expr) _TRACESTUB2(line, expr)
//#define _TRACESTUB _TRACESTUB1(__LINE__)


////////////////////////////////////////////////////////////////////////
//  implement
//

#if defined(_COVER_STUB_IMP)|defined(_TRACE_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)

#define _COVER_BGN_FLAG 0x65766f63	//cove
#define _COVER_END_FLAG 0x7265766f	//over

__declspec(allocate(".cover$__a")) int _cover_bgn_flg = _COVER_BGN_FLAG;
__declspec(allocate(".cover$__z")) int _cover_end_flg = _COVER_END_FLAG;


struct ForEachCoverStubFnPara
{
	BOOL fCoverStub;
	BOOL fFullPath;
	BOOL fSkipStub;
	pfnOutMsg pfnOutMessage;
};

EXTERNC BOOL WINAPI ForEachCoverStubPrintFn(VOID* pv, struct CoverPoint* pCp)
{
	struct ForEachCoverStubFnPara* para=(struct ForEachCoverStubFnPara*)(pv);
	TCHAR buf[MAX_PATH*2];
	DWORD i;
	const TCHAR* pPath;

	if( ( (para->fCoverStub) && (pCp->dwCnt > 0) )
		|| ( (para->fSkipStub) && (pCp->dwCnt == 0) ) )
	{
		if(para->fFullPath)
		{
			pPath=pCp->pszFile;
		}
		else
		{
			pPath=_tcsrchr(pCp->pszFile, '/');
			if( (!(pPath=_tcsrchr(pCp->pszFile, '/')))
				&& (!(pPath=_tcsrchr(pCp->pszFile, '\\'))) )
			{
				pPath=pCp->pszFile;
			}
		}
		
		_sntprintf(buf, sizeof(buf)/sizeof(buf[0]), _T("%s(%s), coverCnt:%d\r\n"), pPath, pCp->pszLine, pCp->dwCnt);

		// convert '/' to '\'
		for(i=0; i<sizeof(buf)/sizeof(buf[0]); i++)
		{
			if('/'==buf[i])
			{
				buf[i]='\\';
			}
		}
		para->pfnOutMessage(buf);
	}

	return TRUE;
}

#endif	//#if defined(_COVER_STUB_IMP)|defined(_TRACE_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)

#if defined(_COVER_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)

BOOL ForEachCoverStub(pfnCoverStubFn pfn, VOID* pv)
{
	int* piCPFind;
	struct CoverPoint* pCp;

//	ASSERT(_COVER_BGN_FLAG==_cover_bgn_flg);

	// find the CoverPoint
	for(piCPFind=(&_cover_bgn_flg+1); piCPFind<&_cover_end_flg; piCPFind++)
	{
		if(COVERPOINT_TAG==*piCPFind)
		{
			break;
		}
	}
	if(piCPFind >= &_cover_end_flg)
	{
		return FALSE;
	}

	while(piCPFind<&_cover_end_flg)
	{
		if(COVERPOINT_TAG==*piCPFind)
		{
			pCp=(struct CoverPoint*)piCPFind;
			if(!(*pfn)(pv, pCp))
			{
				return FALSE;
			}
			piCPFind+=(sizeof(struct CoverPoint)/sizeof(int*));
		}
		else
		{
			piCPFind++;
		}
	}

	return TRUE;
}

VOID CoverStubOutPutInfo(pfnOutMsg pfnOutMessage)
{
	DWORD i;
	struct ForEachCoverStubFnPara para;
	if(!pfnOutMessage)
	{
		pfnOutMessage=OutputDebugString;
	}
	para.pfnOutMessage=pfnOutMessage;

	for(i=0; i<1; i++)
	{
		if(0==i)
		{
			pfnOutMessage(_T("Covered code:\r\n"));
			para.fCoverStub=TRUE;
			para.fSkipStub=FALSE;
		}
		else
		{
			pfnOutMessage(_T("Uncovered code:\r\n"));
			para.fCoverStub=FALSE;
			para.fSkipStub=TRUE;
		}

		ForEachCoverStub(ForEachCoverStubPrintFn, &para);
	}

}

EXTERNC BOOL WINAPI ForEachStubClearFn(VOID* pv, struct CoverPoint* pCp)
{
	pCp->dwCnt=0;
	return TRUE;
}

VOID CoverStubClear()
{
	ForEachCoverStub(ForEachStubClearFn, NULL);
}

VOID GetCoverStubInterface(struct CoverStubInterface* pCvStubIntf)
{
	pCvStubIntf->pfn_CoverStubClear=CoverStubClear;
	pCvStubIntf->pfn_CoverStubOutPutInfo=CoverStubOutPutInfo;
	pCvStubIntf->pfn_ForEachCoverStub=ForEachCoverStub;
}

#endif	//#if defined(_COVER_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)


#if defined(_TRACE_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)


INIT_FAST_LOCK_DECLEAR(g_FastLockTraceStack);
DWORD g_TraceStack[TRACE_STUB_STACK_SIZE_KB*1024];
DWORD g_dwTraceStackId=0;
DWORD g_dwDropped=0;
DWORD g_dwTraced=0;
BOOL g_fTraceStackFull=FALSE;
BOOL g_fTraceEnable=TRACE_STUB_INIT_ENABLE;
DWORD g_dwTraceOption=TRACE_STUB_INIT_OPTION;

struct TraceInfo* WINAPI TraceStubExpand(WORD wExpandDwSize, ...)
{
	DWORD dwStackSize, i;
	DWORD* pExpendData;
	LARGE_INTEGER li;
	struct TraceInfo* pTraceInfo;
    va_list arglist;

    if(!g_fTraceEnable)
    {
        return NULL;
    }

	// get tracestack lock
	if(FALSE==FastLock_Lock(&g_FastLockTraceStack, 1000))
	{
		g_dwDropped++;
		return NULL;
	}

	dwStackSize=(sizeof(struct TraceInfo)/sizeof(DWORD))+wExpandDwSize;
	if((g_dwTraceStackId+dwStackSize)>=(sizeof(g_TraceStack)/sizeof(g_TraceStack[0])))
	{
		g_dwTraceStackId=0;
		g_fTraceStackFull=TRUE;
		g_dwDropped++;					// not accurate
	}
	pTraceInfo=(struct TraceInfo*)(&g_TraceStack[g_dwTraceStackId]);
	g_dwTraceStackId+=dwStackSize;

	// release lock
	FastLock_Release(&g_FastLockTraceStack);

	g_dwTraced++;

	if(TRACE_STUB_OPTION_PERF&g_dwTraceOption)
	{
		QueryPerformanceCounter(&li);
		pTraceInfo->dwPerformCnt=li.LowPart;
	}
	pTraceInfo->wTag=TRACE_INFO_TAG;
	pTraceInfo->wExpandDwSize=wExpandDwSize;

	if(wExpandDwSize)
	{
		pExpendData=(DWORD*)(pTraceInfo+1);
		va_start(arglist, wExpandDwSize);
		for(i=0; i<wExpandDwSize; i++)
		{
			pExpendData[i]=va_arg(arglist, DWORD);
		}
		va_end(arglist);
	}

	return pTraceInfo;

}

VOID TraceStubReset()
{
	g_dwTraced=0;
    g_dwTraceStackId=0;
    g_fTraceStackFull=FALSE;
	g_dwDropped=0;
	memset(&g_TraceStack[0], 0, sizeof(struct TraceInfo));
}

VOID TraceStubDisable()
{
	g_fTraceEnable=FALSE;
}
VOID TraceStubEnable()
{
	g_fTraceEnable=TRUE;
}

VOID TraceStubSetOption(DWORD dwOption)
{
	g_dwTraceOption=dwOption;
}

DWORD TraceStubGetOption()
{
	return g_dwTraceOption;
}

VOID TraceStubGetInfo(DWORD* pdwBufSize, DWORD* pdwTraceNumb, DWORD* pdwDropped)
{
	if(pdwBufSize)
	{
		*pdwBufSize=sizeof(g_TraceStack);
	}

	if(pdwTraceNumb)
	{
		*pdwTraceNumb=g_dwTraced;
	}

	if(pdwDropped)
	{
		*pdwDropped=g_dwDropped;
	}
}

struct ForEachTraceStubFnPara
{
	pfnOutMsg pfnOutMessage;
	BOOL fFullPath;
	DWORD dwPerfermFrq;
};

EXTERNC BOOL WINAPI ForEachTraceStubPrintFn(VOID* pv, struct TraceInfo* pTi)
{
	struct ForEachTraceStubFnPara* pPara=(struct ForEachTraceStubFnPara*)(pv);
	struct CoverPoint* pCp=pTi->pCp;
	LARGE_INTEGER liCnt={0}, liFrq={0};
    DWORD dwS, dwMS, dwNS;

	TCHAR buf[MAX_PATH*2];
	DWORD i;
	int id;

	const TCHAR* pPath;
	if(pPara->fFullPath)
	{
		pPath=pCp->pszFile;
	}
	else
	{
		pPath=_tcsrchr(pCp->pszFile, '/');
		if( (!(pPath=_tcsrchr(pCp->pszFile, '/')))
			&& (!(pPath=_tcsrchr(pCp->pszFile, '\\'))) )
		{
			pPath=pCp->pszFile;
		}
		else
		{
			pPath++;
		}
	}

	liCnt.LowPart=pTi->dwPerformCnt;
	liFrq.LowPart=pPara->dwPerfermFrq;
	PerfermCntToSMN(&liCnt, &liFrq, &dwS, &dwMS, &dwNS);
	id=_sntprintf(buf, ARRAYOF(buf), _T("0x%08x, %s(%s), coverCnt:%d, %d:%d,%d"), pTi->dwPerformCnt, pPath, pCp->pszLine, pCp->dwCnt, dwS, dwMS, dwNS);
	// convert '/' to '\'
	for(i=0; i<sizeof(buf)/sizeof(buf[0]); i++)
	{
		if('/'==buf[i])
		{
			buf[i]='\\';
		}
	}

	for(i=0; i<pTi->wExpandDwSize; i++)
	{
		id+=_sntprintf(buf+id, ARRAYOF(buf)-id, _T(", p%d:0x%x"), i, ((DWORD*)(pTi+1))[i]);
	}
	buf[id]='\r';
	buf[id+1]='\n';
	buf[id+2]=0;

	pPara->pfnOutMessage(buf);

	return TRUE;
}


BOOL ForEachTraceStub(pfnTraceStubFn pfn, VOID* pv)
{
    DWORD dwStartId;
	struct TraceInfo* pTi;
	WORD wExpandDwSize;

    if(g_fTraceStackFull)
    {
		for(dwStartId=g_dwTraceStackId; dwStartId<(sizeof(g_TraceStack)/sizeof(g_TraceStack[0])); dwStartId++)
		{
			pTi=(struct TraceInfo*)(&g_TraceStack[dwStartId]);
			if( (TRACE_INFO_TAG==pTi->wTag)
				&& (COVERPOINT_TAG==pTi->pCp->dwTag) )
			{
				break;
			}
		}

		while( (pTi<(struct TraceInfo*)(&g_TraceStack[sizeof(g_TraceStack)/sizeof(g_TraceStack[0])]))
				&& (TRACE_INFO_TAG==pTi->wTag)
				&& (COVERPOINT_TAG==pTi->pCp->dwTag) )
		{
			if(!pfn(pv, pTi))
			{
				return FALSE;
			}
		
			wExpandDwSize=pTi->wExpandDwSize;
			pTi=(struct TraceInfo*)((BYTE*)pTi+sizeof(struct TraceInfo)+pTi->wExpandDwSize*sizeof(DWORD));
		}
    }

	pTi=(struct TraceInfo*)(&g_TraceStack[0]);
	while( (pTi<(struct TraceInfo*)(&g_TraceStack[sizeof(g_TraceStack)/sizeof(g_TraceStack[0])]))
			&& (TRACE_INFO_TAG==pTi->wTag)
			&&(COVERPOINT_TAG==pTi->pCp->dwTag) )
	{
		if(!pfn(pv, pTi))
		{
			return FALSE;
		}
	
		wExpandDwSize=pTi->wExpandDwSize;
		pTi=(struct TraceInfo*)((BYTE*)pTi+sizeof(struct TraceInfo)+pTi->wExpandDwSize*sizeof(DWORD));
	}

	return TRUE;
}


VOID TraceStubOutPutInfo(pfnOutMsg pfnOutMessage)
{
	struct ForEachTraceStubFnPara TSPara;
	LARGE_INTEGER li;
	TCHAR buf[MAX_PATH];

	if(!pfnOutMessage)
	{
		pfnOutMessage=OutputDebugString;
	}

	_sntprintf(buf, ARRAYOF(buf), _T("dwTraced:%d, dwDropped:%d, TraceStack addr:0x%x, TraceStack size:0x%x\r\n"), g_dwTraced, g_dwDropped, &g_TraceStack, sizeof(g_TraceStack));
	pfnOutMessage(buf);

	TSPara.pfnOutMessage=pfnOutMessage;
	TSPara.fFullPath=FALSE;
	if(QueryPerformanceFrequency(&li))
	{
		TSPara.dwPerfermFrq=li.LowPart;
	}
	else
	{
		TSPara.dwPerfermFrq=1000;
	}

	ForEachTraceStub(ForEachTraceStubPrintFn, &TSPara);
}

#endif	//#if defined(_TRACE_STUB_IMP)|defined(_STUB_IMP)|defined(KEN_LIB_IMP)


#ifdef __cplusplus
}
#endif

#pragma warning( pop ) //#pragma warning(disable : 4996)

#endif	//#ifndef _STUBS_H_

